'use strict';

var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');

/**
 * Create a new instance of Axios
 *
 * @param {Object} instanceConfig The default config for the instance
 */
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  // 拦截器
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

/**
 * Dispatch a request
 *
 * @param {Object} config The config specific for this request (merged with this.defaults)
 */
Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  // 根据传入的参数，config为string时，表示为url，以第二个参数为配置项
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  // 合并默认参数项
  config = mergeConfig(this.defaults, config);

  // Set config.method
  // 设置config的method  此时config组织完整
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  // Hook up interceptors middleware
  // 拦截器的中间件
  var chain = [dispatchRequest, undefined];
  //  把config当做参数给Primise.resolve, 此时promise的状态为resolved
  var promise = Promise.resolve(config);

  // interceptor.fulfilled和interceptor.rejected可能会是undefined
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    // 在chain前面添加
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    // 添加到chain后面
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  // 添加拦截器之后，chain的数组可能是：
  // [
  //   requestFulFulledFn, requestRejectedFn, ...,
  //   dispatchRequest, undefined,
  //   responseFulFulledFn, responseRejectedFn, ...,
  //]

  // 循环执行chain，知道chain长度为0
  while (chain.length) {
    // 一对对的取 每次while都会从chain数组里面取两项，分别作为promise.then的第一个和第二个参数，刚刚好和我们使用InterceptorManager.prototype.use添加拦截器的规则符合

    // 此时需要注意：interceptors的use方法都是才用push添加到handlers里面的，如此就需要了解
    // 关于request的拦截器 --- 在上面代码代用interceptors.request.forEach时，采用的是unshift方法加入到chain，用shift取出的，所以request的拦截器是后进先执行
    // 而response的拦截器 --- 在上面代码代用interceptors.response.forEach时，采用的是push方法加入到chain的，然后通过shift取出，所以response的拦截器是按添加的顺序执行的

    // 约定前提：每一个request拦截器的fulfilled函数都会接受到一个config参数，然后规定返回一个config，这样的话，通过promise实现链式调用时，每个求情拦截器的fulfilled都能拿到config参数
    // 同理，对response拦截器的fulfilled函数也规定了必须返回"response"对象，而第一个response拦截器的fulfilled函数的"response"参数对象是由dispatchRequest提供的(即请求结果)

    // 当两种拦截器抛出错误的时候，都会被下一个拦截器的rejected函数收到，所以，dispatchRequest抛出的错误，将会被response拦截器的rejected接收到
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

Axios.prototype.getUri = function getUri(config) {
  config = mergeConfig(this.defaults, config);
  return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};

// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

module.exports = Axios;
